#include <bits/stdc++.h>
using namespace std;

/*
91. 解码方法
已解答
中等
相关标签
premium lock icon
相关企业
一条包含字母 A-Z 的消息通过以下映射进行了 编码 ：

"1" -> 'A'

"2" -> 'B'

...

"25" -> 'Y'

"26" -> 'Z'

然而，在 解码 已编码的消息时，你意识到有许多不同的方式来解码，因为有些编码被包含在其它编码当中（"2" 和 "5" 与 "25"）。

例如，"11106" 可以映射为：

"AAJF" ，将消息分组为 (1, 1, 10, 6)
"KJF" ，将消息分组为 (11, 10, 6)
消息不能分组为  (1, 11, 06) ，因为 "06" 不是一个合法编码（只有 "6" 是合法的）。
注意，可能存在无法解码的字符串。

给你一个只含数字的 非空 字符串 s ，请计算并返回 解码 方法的 总数 。如果没有合法的方式解码整个字符串，返回 0。

题目数据保证答案肯定是一个 32 位 的整数。

 

示例 1：

输入：s = "12"
输出：2
解释：它可以解码为 "AB"（1 2）或者 "L"（12）。
示例 2：

输入：s = "226"
输出：3
解释：它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
示例 3：

输入：s = "06"
输出：0
解释："06" 无法映射到 "F" ，因为存在前导零（"6" 和 "06" 并不等价）。
 

提示：

1 <= s.length <= 100
s 只包含数字，并且可能包含前导零。
*/

// 法一
class Solution {
public:
    int numDecodings(string s) {
        // dp
        int n = s.size();
        if (n == 0)     return 0;
        // prev_prev对应dp[i-2]，prev对应dp[i-1]
        int prev_prev = 1,  /* dp[0] = 1（空字符串的解码方式）*/ prev = (s[0] != '0') ? 1 : 0;  // dp[1]：第一个字符的解码方式

        // 特殊情况
        if (n == 1) return prev;
        // 当前dp 分情况
        int cur = 0;
        for (int i = 2; i <= n; i++) {
            cur = 0;        // 要每一次之前都重置
            // 单独解码当前的字符
            if (s[i - 1] != '0')    cur += prev;    // 0 不能单独解码

            // 与前一个字符组成两位数解码（s[i-2]和s[i-1]）
            int two_dig = (s[i - 2] - '0') * 10 + (s[i - 1] - '0');
            if (two_dig >= 10 && two_dig <= 26)     cur += prev_prev;   // [10,26]

            // 更新前序的状态 下一轮迭代前的前导处理
            prev_prev = prev;
            prev = cur;
        }
        return prev;
    }
};

// 法二
class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        // 预处理：在字符串前加空格，使索引从1开始（方便处理i-1和i-2的边界情况）
        s = " " + s;
        // 动态规划数组：f[i]表示前i个字符（即s[1..i]）的解码方法数
        vector<int> f(n + 1, 0);
        // 初始化：空字符串有1种解码方式（什么都不做）
        f[0] = 1;        
        // 遍历字符串的每个位置（从1到n，对应原字符串的每个字符）
        for(int i = 1; i < n + 1; i++) {
            // a：当前字符s[i]对应的数字（单独解码时使用）
            int a = s[i] - '0';
            // b：当前字符与前一个字符组成的两位数（s[i-1]和s[i]，组合解码时使用）
            int b = (s[i - 1] - '0') * 10 + s[i] - '0';
            
            // 情况1：当前字符可单独解码（1-9）
            // 若合法，则前i个字符的解码数 += 前i-1个字符的解码数（延续单独解码的方式）
            if(1 <= a && a <= 9) f[i] = f[i - 1];
            
            // 情况2：当前字符与前一个字符可组合解码（10-26）
            // 若合法，则前i个字符的解码数 += 前i-2个字符的解码数（新增组合解码的方式）
            if(10 <= b && b <= 26) f[i] += f[i - 2];
        }
        // 返回整个字符串（前n个字符）的解码方法数
        return f[n];
    }
};